﻿/*
   Copyright 2012 Dmitry Bratus

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Flower.Client;
using Flower.Directory.Host.Default;
using Flower.Directory.Management;
using Flower.Services;
using Flower.Services.Data;
using Flower.Testing;
using NUnit.Framework;
using Jurassic;
using System.Runtime.Serialization;
using Flower.Directory.Util;
using Jurassic.Library;
using Flower.Directory.Scripting.DirectoryEntries;
using System.Threading;

namespace Flower.Directory.Scripting.Tests
{
    [TestFixture]
    public class DirectoryTests
    {
        [DataContract]
        public class MessageType
        {
            [DataMember]
            public int Id { get; set; }

            [DataMember]
            public string Value { get; set; }
        }
        
        private IDirectory _directory;
        private ScriptEngine _engine;

        #region Setup

        [SetUp]
        public void Setup()
        {
            var dir = new MemoryDirectory.Directory(new ScriptEnginePool(1));

            new DirectoryBuilder(dir)
                .Root("/Sets/Shared")
                    .Set("SharedSet", typeof(MessageType).FullName, 3)
                    .Set("Src", typeof(int).FullName, 0)
                    .Set("Dest", typeof(int).FullName, 0)
                .End();

            _directory = dir;

            _engine = Scripting.Setup.CreateEngine();
            _engine.SetGlobalValue("$dir", new DirectoryObject(_engine, _directory));
        }

        #endregion

        [Test]
        public void GetPathById()
        {
            Assert.IsTrue(_engine.Evaluate<string>(@"$dir.getPathById('#0')") == "/");
        }

        [Test]
        public void Exists()
        {
            Assert.IsTrue(_engine.Evaluate<bool>(@"$dir.exists('/Sets/Shared')"));

            var existenceDate = DateTime.Now;
            Thread.Sleep(300);

            var entry = new Message
            {
                Name = "MessageNameX",
                Data =
                    (new MessageType
                    {
                        Id = 123,
                        Value = "Payload"
                    }).ToBlob(BlobFormat.BinXml)
            };

            _engine.SetGlobalValue("newMessage", JsConvert.EntryToObject(_engine, entry));
            _engine.SetGlobalValue("existenceDate", existenceDate);

            var id = _engine.Evaluate<string>("$dir.save('/Sets/Shared/SharedSet', newMessage)");

            Assert.IsTrue(_engine.Evaluate<bool>(@"$dir.exists('/Sets/Shared/SharedSet/MessageNameX', existenceDate)"));

            Thread.Sleep(300);

            _engine.SetGlobalValue("nonExistenceDate", DateTime.Now);
            Assert.IsFalse(_engine.Evaluate<bool>(@"$dir.exists('/Sets/Shared/SharedSet/MessageNameX', nonExistenceDate)"));
        }

        [Test]
        public void SaveGet()
        {
            var entry = new Message
            {
                Name = "MessageName",
                Data =
                    (new MessageType
                    {
                        Id = 123,
                        Value = "Payload"
                    }).ToBlob(BlobFormat.BinXml)
            };

            _engine.SetGlobalValue("newMessage", JsConvert.EntryToObject(_engine, entry));

            var id = _engine.Evaluate<string>("$dir.save('/Sets/Shared/SharedSet', newMessage)");

            Assert.IsTrue(id != null);
            Assert.IsTrue(id.StartsWith("#"));

            var obj = _engine.Evaluate<ObjectInstance>("$dir.get('/Sets/Shared/SharedSet/MessageName')");

            Assert.IsTrue(obj != null);
            Assert.IsTrue(obj is MessageObject);

            var obtainedEntry = (Message)JsConvert.ObjectToEntry(obj);

            Assert.IsTrue(obtainedEntry.Id == id);
        }

        [Test]
        public void MoveRemoveCountChildren()
        {
            for (int i = 0; i < 10; i++)
            {
                _engine.SetGlobalValue
                (
                    "x", 
                    JsConvert.EntryToObject
                    (
                        _engine,
                        new Message { Name = i.ToString(), Data = i.ToBlob(BlobFormat.TextXml) }
                    )
                );
                _engine.Evaluate<string>(@"$dir.save('/Sets/Shared/Src', x)");
            }

            var count = _engine.Evaluate<int>(@"$dir.countChildren('/Sets/Shared/Src', DET_ALL)");
            Assert.IsTrue(count == 10);

            count = _engine.Evaluate<int>
            (
                @"for (var i = 0; i < 10; i++) {" +
                @"    $dir.move('/Sets/Shared/Src/' + i, '/Sets/Shared/Dest');" +
                @"}" +
                @"$dir.countChildren('/Sets/Shared/Dest', DET_ALL);"
            );
            Assert.IsTrue(count == 10);

            count = _engine.Evaluate<int>(@"$dir.countChildren('/Sets/Shared/Dest', DET_ALL)");
            Assert.IsTrue(count == 10);

            _engine.Execute
            (
                @"var children = $dir.getChildren('/Sets/Shared/Dest', DET_ALL, SORT_DEFAULT, ASC, 0, 0);" +
                @"var result = children.length > 0;" +
                @"for (var i = 0; i < children.length; i++) {" +
                @"    result &= children[i].name == (i).toString();" +
                @"}"
            );

            Assert.IsTrue(_engine.GetGlobalValue<bool>("result"));

            var setsFolder = _engine.Evaluate<ObjectInstance>("$dir.getAncestor('/Sets/Shared/Dest/0', 2, DET_FOLDER)");

            Assert.IsTrue(setsFolder != null);
            Assert.IsTrue(setsFolder is FolderObject);
            Assert.IsTrue(((FolderObject)setsFolder).Entry.Name == "Sets");
        }
    }
}
